home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-20 / pmpsrc11.zip / AX25LAPB.C < prev    next >
Text File  |  1991-07-30  |  18KB  |  806 lines

  1. /*
  2.     AX25LABP.C -- Implements AX.25 link level routines
  3.  
  4.   Poor Man's Packet (PMP)
  5.   Copyright (c) 1991 by Andrew C. Payne    All Rights Reserved.
  6.  
  7.   Permission to use, copy, modify, and distribute this software and its
  8.   documentation without fee for NON-COMMERCIAL AMATEUR RADIO USE ONLY is hereby
  9.   granted, provided that the above copyright notice appear in all copies.
  10.   The author makes no representations about the suitability of this software
  11.   for any purpose.  It is provided "as is" without express or implied warranty.
  12.  
  13.     August, 1989
  14.     Andrew C. Payne
  15. */
  16.  
  17. /* ----- Includes ----- */
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <conio.h>
  21. #include <bios.h>
  22. #include <mem.h>
  23. #include <alloc.h>
  24. #include <string.h>
  25. #include "pmp.h"
  26.  
  27. static void EmptyDQ(void);
  28.  
  29. /* ----- Local Variables ----- */
  30.  
  31. static struct ax25_packet *AX25_TXQueue[8];    /* TX Queue */
  32.  
  33. #define INC(x)    (((x)+1) & 7)            /* mod 7 increment */
  34. #define DEC(x)    (((x)-1) & 7)            /* mod 7 decrement */
  35.  
  36. #define SECONDS(x)    (BiosTime() + (x) * BIOSSEC)
  37.  
  38. /* ----- Timer Stuff ----- */
  39.  
  40. /* StartT1()
  41.     Starts timer T1 going.  The T1 timeout is calculated from the
  42.     FRACK parameter, the number of digis in the connect path, AND the
  43.     number of bytes currently sitting in the TX queue (so that the
  44.     timer starts up *after* the TX contents are sent).
  45. */
  46. void StartT1(void)
  47. {
  48.     AX25_Control.t1 = SECONDS(Frack*(2 * AX25_Control.header.ndigis + 1)) +
  49.         TXQBytesInQ() * 8 / 1200 * BIOSSEC;
  50. }
  51.  
  52. /* StopT1()
  53.     Stops timer T1.
  54. */
  55. #define StopT1()    (AX25_Control.t1 = 0)
  56.  
  57. /* StartT3()
  58.     Starts timer T3 (keep-alive).
  59. */
  60. static void StartT3(void)
  61. {
  62.     AX25_Control.t3 = SECONDS(Check);
  63. }
  64.  
  65. /* StopT3()
  66.     Stops timer T3.
  67. */
  68. #define StopT3()    (AX25_Control.t3 = 0)
  69.  
  70. /* Connected()
  71.     Returns TRUE if the link is in the connected (information xfer)
  72.     state.
  73. */
  74. int Connected(void)
  75. {
  76.     switch(AX25_Control.state) {
  77.         case CONNECTED:
  78.         case RECOVERY:
  79.             return TRUE;
  80.         default:
  81.             return FALSE;
  82.     }
  83. }
  84.  
  85. /* DumpState()
  86.     Shows the state of the TXQ on the status line (sort of like an
  87.     expanded 'STA' light).
  88.  
  89.     This routine was originally for debugging, but it is kind of neat,
  90.     so I'm leaving it in.
  91. */
  92. static void DumpState(void)
  93. {
  94.     int    i;
  95.     char    s[10];
  96.     static    char blank[] = "        ";
  97.  
  98. /* go thorough all 8 TXQ entries */
  99.     if(Connected()) {
  100.         for(i=0; i<8; i++)
  101.             if(AX25_TXQueue[i] != NULL)
  102.                 s[i] = '*';
  103.             else
  104.                 s[i] = '-';
  105.     } else
  106.         strcpy(s,blank);
  107.  
  108. /* show the string on the status line */
  109.     putstring(64,25,8,0x70,s);
  110. }
  111.  
  112. /* ----- Subroutines ----- */
  113.  
  114. /* AX25_Init()
  115.     Initializes the AX25 stuff in this module.
  116. */
  117. void AX25_Init(void)
  118. {
  119.     int    i;
  120.  
  121.     for(i=0; i<8; i++)
  122.         AX25_TXQueue[i] = NULL;
  123. }
  124.  
  125. /* RecACK(n)
  126.     Given a sequence number, acknowledges all packets up to, but not
  127.     including that number.
  128.  
  129.     If all frames are ACKed, T1 is axed, else T1 is restarted.
  130. */
  131. static void RecACK(int n)
  132. {
  133.     int    i;
  134.     int    ackcount;
  135.  
  136. /* go backwards through the AX25_TXQueue freeing ACKed packets */
  137.     ackcount = 0;
  138.     i = DEC(n);
  139.     while(AX25_TXQueue[i] != NULL) {    /* was ACKed */
  140.         free(AX25_TXQueue[i]);
  141.         AX25_TXQueue[i] = NULL;
  142.         i = DEC(i);
  143.         ackcount++;
  144.         AX25_Control.unack--;
  145.         AX25_Control.qsize--;
  146.     }
  147.  
  148.     if(ackcount)
  149.         AX25_Control.retries = 0;    /* reset retries */
  150.  
  151. /* adjust T1 */
  152.     if(AX25_Control.unack == 0) {
  153.         StopT1();            /* no outstanding */
  154.         StartT3();
  155.     } else if(ackcount)
  156.         StartT1();            /* restart T1 */
  157.  
  158.     DumpState();
  159. }
  160.  
  161. /* FlushTXQ()
  162.     Discards any entries in the TX Queue.  Done after a disconnect and
  163.     before a connect.
  164. */
  165. static void FlushTXQ(void)
  166. {
  167.     int    i;
  168.  
  169. /* free up any non-NULL entries */
  170.     for(i=0; i<8; i++) {
  171.         if(AX25_TXQueue[i] != NULL) {
  172.             free(AX25_TXQueue[i]);
  173.             AX25_TXQueue[i] = NULL;
  174.         }
  175.     }
  176.     AX25_Control.qsize = AX25_Control.unack = 0;
  177.  
  178.     DumpState();
  179. }
  180.  
  181. /* SendControl(cmd,cmdresp)
  182.     Sends a command/response packet to the current destination specified
  183.     in the AX25_Control record.
  184.  
  185.     If there is a pending ACK, it is cleared.
  186. */
  187. static void SendControl(int cmd, int cmdresp)
  188. {
  189. /* add rec sequence to S packets */
  190.     if((cmd & 3) == 1) {
  191.         AX25_Control.response = 0;
  192.         cmd |= AX25_Control.vr << 5;
  193.     }
  194.  
  195. /* set up the header */
  196.     AX25_Control.header.dlen = 0;
  197.     AX25_Control.header.cont = cmd;
  198.     AX25_Control.header.cmdresp = cmdresp;
  199.  
  200. /* enqueue packet */
  201.     SendAX25(&AX25_Control.header);
  202. }
  203.  
  204. /* ReplyDM(p)
  205.     Given an incoming packet (with path), sends a Disconnected Mode
  206.     reply.
  207. */
  208. static void ReplyDM(struct ax25_packet *p)
  209. {
  210.     struct ax25_packet    p1;
  211.  
  212.     ReversePath(&p1,p);
  213.     p1.dlen = 0;
  214.     p1.cont = DM|PF;
  215.     p1.cmdresp = RESPONSE;
  216.  
  217.     SendAX25(&p1);
  218. }
  219.  
  220. /* ChangeState(s)
  221.     Changes to the state given.  Performs any processing needed for the
  222.     state change.
  223.  
  224.     NOTE:  if the change is to the CONNECTED state from any state but
  225.            RECOVERY, the link is reset.
  226. */
  227. void ChangeState(int s)
  228. {
  229. /* already in this state?, if so, no change */
  230.     if(AX25_Control.state == s)
  231.         return;
  232.  
  233. /* notify the user what's happening */
  234.     NotifyStatus(AX25_Control.state, s);
  235.  
  236. /* for connects and disconnects, initialize */
  237.     if(s == DISCONNECTED || (s == CONNECTED && AX25_Control.state != RECOVERY)) {
  238.         StopT1();
  239.         StopT3();
  240.         AX25_Control.vs = 0;
  241.         AX25_Control.vr = 0;
  242.         AX25_Control.tqs = 0;
  243.         AX25_Control.response = 0;
  244.         AX25_Control.retries = 0;
  245.         AX25_Control.remotebusy = FALSE;
  246.         DQInit(&AX25_Control.dq);    /* init data queue */
  247.         FlushTXQ();            /* flush queue */
  248.     }
  249.  
  250. /* set current state */
  251.     AX25_Control.state = s;
  252.     DumpState();
  253. }
  254.  
  255. /*  AX25_Open()
  256.     Starts setting up an AX25 connection.
  257.  
  258.     NOTE:  Assumes that the AX25_Control record has already been set up.
  259. */
  260. void AX25_Open(void)
  261. {
  262.     switch(AX25_Control.state) {
  263.     case DISCONNECTED:
  264.     case SETUP:
  265.         SendControl(SABM,COMMAND);        /* send connect packet */
  266.         StartT1();
  267.         ChangeState(SETUP);
  268.         break;
  269.     }
  270. }
  271.  
  272. /* ----- Incoming Packet Handling ----- */
  273.  
  274. /* AX25_Output()
  275.     Writes out any I frames sitting in the AX25_TXQueue, but that have
  276.     not been transmitted.
  277.  
  278.     If an I-frame is sent, an implicit ACK is also sent, so the
  279.     'response' variable is cleared.
  280. */
  281. static void AX25_Output(void)
  282. {
  283.     int    i;
  284.  
  285. /* should be in the CONNECTED or RECOVERY state and remote not busy */
  286.     if(AX25_Control.state != CONNECTED && AX25_Control.state != RECOVERY
  287.         && !AX25_Control.remotebusy)
  288.             return;
  289.  
  290. /* send any frames ahead of us in the TX queue */
  291.     i = AX25_Control.vs;        /* next frame to send */
  292.     while(AX25_TXQueue[i] != NULL) {
  293.  
  294. /* set up the control field and send the packet */
  295.         AX25_TXQueue[i]->cont = (i << 1) | (AX25_Control.vr << 5) | I;
  296.         SendAX25(AX25_TXQueue[i]);
  297.  
  298. /* the ack was piggybacked on the I frame... */
  299.         AX25_Control.response = 0;
  300.  
  301.         i = INC(i);
  302.         AX25_Control.unack++;
  303.  
  304. /* if T1 is not running, start it up */
  305.         if(AX25_Control.t1 == 0) {
  306.             StartT1();
  307.             StopT3();
  308.         }
  309.     }
  310.     AX25_Control.vs = i;
  311. }
  312.  
  313. /* AX25_Flush()
  314.     Called once at the end of each RXQueue pass.   This routine sends
  315.     any enqueued I packets (if the remote is not busy) and any queued
  316.     response.
  317. */
  318. void AX25_Flush(void)
  319. {
  320. /* write any pending output */
  321.     EmptyDQ();            /* copy from DQ */
  322.     AX25_Output();
  323.  
  324. /* if a pending ack left to send, send it */
  325.     if(AX25_Control.response)
  326.         SendControl(AX25_Control.response,RESPONSE);
  327. }
  328.  
  329. /* upcall(p, len)
  330.     Called for each valid incoming packet.
  331. */
  332. static void upcall(byte *p, int len)
  333. {
  334. #ifdef REMOTE
  335.     char    s[80];
  336. #endif
  337.     eol_in(EOL_CR, p, len);        /* convert EOLs */
  338.     uputtext(NormalAttr, p, len);
  339.  
  340. #ifdef REMOTE
  341.     if(p[0] == '/' && p[1] == '/') {
  342.         memcpy(s, p, len);
  343.         s[len] = '\0';        /* terminate */
  344.         command(s+2);
  345.     }
  346. #endif
  347. }
  348.  
  349. /* AX25_Incoming(p)
  350.     Called with a pointer to a level 2 packet for each incoming packet
  351.     addressed to us.
  352. */
  353. void AX25_Incoming(struct ax25_packet *p2)
  354. {
  355.     int    ftype;        /* frame type */
  356.     int    poll;        /* command, poll? */
  357.     int    final;        /* reply, final? */
  358.     int    nr;        /* rec'd incoming frames */
  359.     int    ns;
  360.     int    other;        /* TRUE if from other station */
  361.  
  362.     other = !CompAX25Addr(&p2->source, &AX25_Control.header.dest);
  363.     ftype = FrameType(p2->cont);        /* get frame type */
  364.  
  365. /* set up the poll or final flags */
  366.     poll = final = FALSE;
  367.     if(p2->cont & PF) {
  368.         if(p2->cmdresp == COMMAND)
  369.             poll = TRUE;
  370.         else
  371.             final = TRUE;
  372.     }
  373.  
  374. /* get the send/rec sequences for later use */
  375.     nr = (p2->cont >> 5) & 7;
  376.     ns = (p2->cont >> 1) & 7;
  377.  
  378. /* increment the RX counters */
  379.     switch(ftype) {
  380.         case REJ:
  381.             RXREJ++;
  382.             break;
  383.         case FRMR:
  384.             RXFRMR++;
  385.             break;
  386.     }
  387.  
  388. /* trap stray packets while connected */
  389.     if(AX25_Control.state != DISCONNECTED && !other) {
  390.         ReplyDM(p2);
  391.         return;
  392.     }
  393.  
  394. /* handle the packet, depending on the mode we are in */
  395.     switch(AX25_Control.state) {
  396.  
  397. /* disconnected mode */
  398.     case DISCONNECTED:
  399.         switch(ftype) {
  400.         case SABM:        /* incoming connect */
  401.             ReversePath(&AX25_Control.header,p2);
  402.             AX25_Control.header.pid = PID_TEXT;
  403.             ChangeState(CONNECTED);
  404.             SendControl(UA|PF,RESPONSE);
  405.             SendWelcome();
  406.             StartT3();
  407.             break;
  408.         case DM:
  409.             break;
  410.         default:        /* don't bother us! */
  411.             if(poll)
  412.                 ReplyDM(p2);
  413.             break;
  414.         }
  415.         break;
  416.  
  417. /* link setup mode */
  418.     case SETUP:
  419.         switch(ftype) {
  420.         case SABM:        /* simultaneous connect */
  421.             SendControl(UA|PF,RESPONSE);
  422.             ChangeState(CONNECTED);
  423.             SendWelcome();
  424.             StartT3();
  425.             break;
  426.         case DISC:        /* disconnect */
  427.             SendControl(DM|PF,RESPONSE);
  428.             AX25_Control.dreason = DISC_BUSY;
  429.             ChangeState(DISCONNECTED);
  430.             break;
  431.         case UA:        /* connection accept */
  432.             ChangeState(CONNECTED);
  433.             StartT3();
  434.             break;
  435.         case DM:        /* connection refused */
  436.             AX25_Control.dreason = DISC_BUSY;
  437.             ChangeState(DISCONNECTED);
  438.             break;
  439.         default:
  440.             break;        /* ignore */
  441.         }
  442.         break;
  443.  
  444. /* disconnect pending mode */
  445.     case DISCONNECTPEND:
  446.         switch(ftype) {
  447.             case SABM:
  448.                 SendControl(DM|PF,RESPONSE);
  449.                 break;
  450.             case DISC:
  451.                 SendControl(UA|PF,RESPONSE);
  452.                 break;
  453.             case UA:
  454.             case DM:
  455.                 AX25_Control.dreason = DISC_LOCAL;
  456.                 ChangeState(DISCONNECTED);
  457.                 break;
  458.             default:
  459.                 if(poll) {
  460.                     SendControl(DM|PF,RESPONSE);
  461.                     AX25_Control.dreason = DISC_LOCAL;
  462.                     ChangeState(DISCONNECTED);
  463.                 }
  464.                 break;
  465.         }
  466.         break;
  467.  
  468. /* "we are connected" mode */
  469.     case CONNECTED:
  470.         switch(ftype) {
  471.         case SABM:        /* link reset */
  472.             SendControl(UA|PF,RESPONSE);
  473.             ChangeState(CONNECTED);
  474.             StartT3();
  475.             break;
  476.         case DISC:
  477.             SendControl(UA|PF,RESPONSE);
  478.             AX25_Control.dreason = DISC_REMOTE;
  479.             ChangeState(DISCONNECTED);
  480.             break;
  481.         case DM:
  482.             AX25_Control.dreason = DISC_REMOTE;
  483.             ChangeState(DISCONNECTED);
  484.             break;
  485.         case UA:
  486.             AX25_Control.remotebusy = FALSE;
  487.             /* ignore */
  488.             break;
  489.         case FRMR:            /* frame reject */
  490.             uprintf(InvAttr,"** Fatal -- Frame reject received.  Resetting link...\n");
  491.             ChangeState(SETUP);
  492.             AX25_Open();        /* reset link */
  493.             break;
  494.         case REJ:
  495.             RecACK(nr);        /* ACK our frames */
  496.             if(poll)
  497.                 SendControl(RR|PF,RESPONSE);
  498.             StopT1();        /* re-xmit */
  499.             AX25_Control.vs -= AX25_Control.unack;
  500.             AX25_Control.vs &= 7;
  501.             AX25_Control.unack = 0;
  502.             AX25_Control.remotebusy = FALSE;
  503.             StartT3();
  504.             break;
  505.         case RNR:
  506.         case RR:
  507.             AX25_Control.remotebusy = (ftype == RNR);
  508.             RecACK(nr);        /* ACK our frames */
  509.             if(poll)        /* respond to polls */
  510.                 SendControl(RR|PF,RESPONSE);
  511.             break;
  512.         case I:
  513.             RecACK(nr);        /* ACK our frames */
  514.             if(ns == AX25_Control.vr) { /* in sequence? */
  515.                 upcall(p2->data, p2->dlen);
  516.                 AX25_Control.vr = INC(AX25_Control.vr);
  517.                 if(poll)
  518.                     SendControl(RR|PF, RESPONSE);
  519.                 else
  520.                     AX25_Control.response = RR;
  521.             } else {            /* out of seq */
  522.                 if(poll)
  523.                     SendControl(REJ|PF, RESPONSE);
  524.                 else
  525.                     AX25_Control.response = REJ;
  526.             }
  527.     }
  528.     break;
  529.  
  530. /* "can we get out of this mess?" mode */
  531.     case RECOVERY:
  532.         switch(ftype) {
  533.         case SABM:        /* link reset */
  534.             SendControl(UA|PF,RESPONSE);
  535.             ChangeState(CONNECTED);
  536.             StartT3();
  537.             break;
  538.         case DISC:
  539.             SendControl(UA,RESPONSE);
  540.             AX25_Control.dreason = DISC_REMOTE;
  541.             ChangeState(DISCONNECTED);
  542.             break;
  543.         case DM:
  544.             AX25_Control.dreason = DISC_REMOTE;
  545.             ChangeState(DISCONNECTED);
  546.             break;
  547.         case UA:
  548.             /* ignore */
  549.             AX25_Control.remotebusy = FALSE;
  550.             break;
  551.         case FRMR:
  552.             uprintf(InvAttr,"** Fatal -- Frame reject received.  Resetting link...\n");
  553.             ChangeState(SETUP);
  554.             AX25_Open();        /* reset link */
  555.             break;
  556.         case RNR:
  557.         case RR:
  558.             AX25_Control.remotebusy = (ftype == RNR);
  559.             RecACK(nr);
  560.             if(final) {            /* got a reply */
  561.                 StopT1();
  562.                 if(AX25_Control.unack) {
  563.                     AX25_Control.vs -= AX25_Control.unack;
  564.                     AX25_Control.vs &= 7;
  565.                     AX25_Control.unack = 0;
  566.                 } else {
  567.                     ChangeState(CONNECTED);
  568.                     StartT3();
  569.                 }
  570.             } else {
  571.                 if(poll)         /* respond to poll */
  572.                     SendControl(RR|PF,RESPONSE);
  573.                 if(AX25_Control.t1 == 0)
  574.                     StartT1();
  575.             }
  576.             break;
  577.         case REJ:
  578.             RecACK(nr);
  579.             if(final) {
  580.                 StopT1();
  581.                 if(AX25_Control.unack) {
  582.                     AX25_Control.vs -= AX25_Control.unack;
  583.                     AX25_Control.vs &= 7;
  584.                     AX25_Control.unack = 0;
  585.                 } else {
  586.                     ChangeState(CONNECTED);
  587.                     StartT3();
  588.                 }
  589.             } else {
  590.                 if(poll)        /* enqueue reply */
  591.                     SendControl(RR|PF,RESPONSE);
  592.                 if(AX25_Control.unack) {
  593.                     AX25_Control.vs -= AX25_Control.unack;
  594.                     AX25_Control.vs &= 7;
  595.                     AX25_Control.unack = 0;
  596.                 }
  597.                 if(AX25_Control.t1 == 0)
  598.                     StartT1();
  599.             }
  600.             AX25_Control.remotebusy = FALSE;
  601.             break;
  602.         case I:
  603.             RecACK(nr);
  604.  
  605.             if(AX25_Control.t1 == 0)    /* keep T1 */
  606.                 StartT1();
  607.             if(ns == AX25_Control.vr) { /* in sequence? */
  608.                 upcall(p2->data, p2->dlen);
  609.                 AX25_Control.vr = INC(AX25_Control.vr);
  610.                 if(poll)
  611.                     SendControl(RR|PF, RESPONSE);
  612.                 else
  613.                     AX25_Control.response = RR;
  614.             } else {            /* out of seq */
  615.                 if(poll)
  616.                     SendControl(REJ|PF, RESPONSE);
  617.                 else
  618.                     AX25_Control.response = REJ;
  619.             }
  620.             break;
  621.         }
  622.         break;
  623.     }
  624. }
  625.  
  626. /* AX25_Close()
  627.     Start closing an AX25 connection.
  628. */
  629. void AX25_Close(void)
  630. {
  631.     switch(AX25_Control.state) {
  632.     case DISCONNECTED:            /* ignore */
  633.         break;
  634.     case DISCONNECTPEND:
  635.         AX25_Control.dreason = DISC_LOCAL;
  636.         ChangeState(DISCONNECTED);
  637.         break;
  638.     case CONNECTED:                /* start disconnect */
  639.     case RECOVERY:
  640.     case SETUP:
  641.         AX25_Control.retries =  0;
  642.         SendControl(DISC,COMMAND);
  643.         StartT1();
  644.         ChangeState(DISCONNECTPEND);
  645.         break;
  646.     }
  647. }
  648.  
  649. /* PollRemote()
  650.     Called when T1 times out in CONNECTED or RECOVERY mode to poll
  651.     the remote DXE.
  652. */
  653. static void PollRemote(void)
  654. {
  655.     int    i;
  656.  
  657. /* if data sitting in the queue, use it */
  658.     if(AX25_Control.unack) {
  659.         i = (AX25_Control.vs - AX25_Control.unack) & 0x7;
  660.         if(AX25_TXQueue[i]->dlen <= Pthresh) {
  661.  
  662. /* set up the control field and send the packet */
  663.             AX25_TXQueue[i]->cont = (i << 1) | (AX25_Control.vr << 5) | I | PF;
  664.             SendAX25(AX25_TXQueue[i]);
  665.             StartT1();
  666.             return;
  667.         }
  668.     }
  669.  
  670. /* else, just send RR w/ poll */
  671.     SendControl(RR|PF,COMMAND);
  672.     StartT1();
  673. }
  674.  
  675. /* T1Expire()
  676.     Gets called when T1 or T3 times out.
  677. */
  678. static void T1Expire(void)
  679. {
  680.     switch(AX25_Control.state) {
  681.         case SETUP:        /* link setup */
  682.             if(Retry != 0 && AX25_Control.retries >= Retry) {
  683.                 AX25_Control.dreason = DISC_TIMEOUT;
  684.                 ChangeState(DISCONNECTED);
  685.             } else {
  686.                 AX25_Control.retries++;
  687.                 SendControl(SABM|PF,COMMAND);
  688.                 StartT1();
  689.             }
  690.             break;
  691.         case DISCONNECTPEND:    /* disconnect pending */
  692.             if(Retry != 0 && AX25_Control.retries >= Retry) {
  693.                 AX25_Control.dreason = DISC_TIMEOUT;
  694.                 ChangeState(DISCONNECTED);
  695.             } else {
  696.                 AX25_Control.retries++;
  697.                 SendControl(DISC|PF,COMMAND);
  698.                 StartT1();
  699.             }
  700.             break;
  701.         case CONNECTED:
  702.             AX25_Control.retries = 0;
  703.         case RECOVERY:        /* poll them */
  704.             if(Retry != 0 && AX25_Control.retries >= Retry) {
  705.                 SendControl(DM|PF,RESPONSE);
  706.                 AX25_Control.dreason = DISC_TIMEOUT;
  707.                 ChangeState(DISCONNECTED);
  708.             } else {
  709.                 AX25_Control.retries++;
  710.                 PollRemote();
  711.                 ChangeState(RECOVERY);
  712.             }
  713.             break;
  714.     }
  715. }
  716.  
  717. /* AX25_Expire(n)
  718.     This routine gets called when one of the timers expires.
  719.     'n' is the timer number.
  720. */
  721. void AX25_Expire(int n)
  722. {
  723.     switch(n) {
  724.         case 1:
  725.             StopT1();
  726.             T1Expire();
  727.             break;
  728.         case 2:
  729.             AX25_Control.t2 = 0;
  730.             break;
  731.         case 3:
  732.             StopT3();
  733.             T1Expire();
  734.             break;
  735.     }
  736. }
  737.  
  738. /* ----- Link Transmission ----- */
  739.  
  740. /* AX25QFull()
  741.     Returns TRUE if the AX.25 send queue is full (e.g. MaxFrames
  742.     outstanding).
  743. */
  744. int AX25QFull(void)
  745. {
  746.     return AX25_Control.qsize >= MaxFrame;
  747. }
  748.  
  749. /* LinkSend(p,len)
  750.     Sends data across a connected AX.25 link.  If text link, performs
  751.     appropriate EOL conversions.
  752.  
  753.     Returns TRUE if error (not connected).
  754. */
  755. int LinkSend(byte *p, int len)
  756. {
  757.     struct dqentry *dq;
  758.  
  759. /* gotta be connected to do this stuff */
  760.     if(!Connected())
  761.         return TRUE;
  762.  
  763.     DQAdd(&AX25_Control.dq, p, len);
  764.  
  765.     if(AX25_Control.type == TEXT) {
  766.         dq = DQFirst(&AX25_Control.dq);
  767.         eol_out(EOL_CR, dq->data, dq->len);
  768.     }
  769.  
  770.     return FALSE;            /* no errors */
  771. }
  772.  
  773. /* EmptyDQ()
  774.     Empties data, if any from the DQ into the AX.25 TX queue.
  775.  
  776.     Ultimately, will merge packets in an attempt to get maximum sized
  777.     packets.
  778. */
  779. static void EmptyDQ(void)
  780. {
  781.     struct    ax25_packet    *ap;
  782.     int            len;
  783.  
  784. /* loop until AX.25 queue full or DQueue empty */
  785.     while(!AX25QFull() && AX25_Control.dq.len) {
  786.  
  787. /* build an AX.25 packet, as long as possible */
  788.         len = min(AX25_Control.dq.len, Paclen);
  789.         ap = malloc(sizeof(struct ax25_packet) + len);
  790.         memcpy(ap,&AX25_Control.header,sizeof(struct ax25_packet));
  791.         ap->dlen = len;
  792.         ap->pid = PID_TEXT;
  793.         ap->cmdresp = COMMAND;
  794.         ap->cont = (AX25_Control.vr<<5) | (AX25_Control.tqs<<1) | I;
  795.  
  796. /* fill AX.25 packet from DQueue */
  797.         DQExtract(&AX25_Control.dq, ap->data, len);
  798.  
  799. /* add the packet to the AX.25 queue */
  800.         AX25_TXQueue[AX25_Control.tqs] = ap;
  801.         AX25_Control.tqs = INC(AX25_Control.tqs);
  802.         AX25_Control.qsize++;
  803.         DumpState();
  804.     }
  805. }
  806.